package cn.lg.soar.common.data;

import java.util.Arrays;

/**
 * 位集合
 * 类似 java.util.BitSet 实现，为了节省内存空间而设计
 * java.util.BitSet是以 long 为最小单元，这里以 byte 为最小单元
 * 特点大数据运算不如 java.util.BitSet，8 位以内的数据运算性能极高，更好的内存利用率
 * 使用哪个 BitSet 要看具体业务，需要大量实例多用于查询的推荐使用此 BitSet，极大的节省空间
 * @author luguoxiang
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class BitSet implements java.io.Serializable {

    private final static int[] SETGET = new int[]{ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
    private final static int[] CLEAR = new int[]{ 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe };

    private byte[] bytes;

    /**
     * 获取指定索引的位值
     * @param index
     * @return
     */
    public boolean get(int index) {

        int i = index >>> 3;
        if (i >= bytes.length) {
            enlargeSize(i + 1);
        }
        return (bytes[ i ] & SETGET[ index & 0x07 ]) != 0;

    }

    /**
     * 把指定索引的位设置为 1
     * @param index
     */
    public void set(int index) {

        int i = index >>> 3;
        if (i >= bytes.length) {
            enlargeSize(i + 1);
        }
        bytes[ i ] = (byte) (bytes[ i ] | SETGET[ index & 0x07 ]);
    }

    /**
     * 把所有的位都设置为 1
     */
    public void set() {
        for (int j = 0; j < bytes.length; j++) {
            bytes[ j ] = -1;
        }
    }

    /**
     * 把指定索引的位设置为 0
     * @param index
     */
    public void clear(int index) {

        int i = index >>> 3;
        if (i >= bytes.length) {
            enlargeSize(i + 1);
            return;
        }
        bytes[ i ] = (byte) (bytes[ i ] & CLEAR[ index & 0x07 ]);
    }

    /**
     * 把所有位设置为 0
     */
    public void clear() {
        for (int j = 0; j < bytes.length; j++) {
            bytes[ j ] = 0;
        }
    }

    /**
     * 返回第一个为 1 的位的索引
     * @return
     */
    public int firstSet(){
        int len = 0;
        while (len < bytes.length && bytes[ len++ ] == 0);
        len = len - 1;
        byte b = bytes[ len ];
        if ((b & 0x80) == 0x80) return (len << 3);
        if ((b & 0x40) == 0x40) return (len << 3) + 1;
        if ((b & 0x20) == 0x20) return (len << 3) + 2;
        if ((b & 0x10) == 0x10) return (len << 3) + 3;
        if ((b & 0x08) == 0x08) return (len << 3) + 4;
        if ((b & 0x04) == 0x04) return (len << 3) + 5;
        if ((b & 0x02) == 0x02) return (len << 3) + 6;
        return (len << 3) + 7;
    }

    /**
     * 返回第一个为 0 的位的索引
     * @return
     */
    public int firstClear(){
        int len = 0;
        while (len < bytes.length && bytes[ len++ ] == -1);
        len = len - 1;
        byte b = bytes[ len ];
        if ((b & 0x80) == 0) return (len << 3);
        if ((b & 0x40) == 0) return (len << 3) + 1;
        if ((b & 0x20) == 0) return (len << 3) + 2;
        if ((b & 0x10) == 0) return (len << 3) + 3;
        if ((b & 0x08) == 0) return (len << 3) + 4;
        if ((b & 0x04) == 0) return (len << 3) + 5;
        if ((b & 0x02) == 0) return (len << 3) + 6;
        return (len << 3) + 7;
    }

    /**
     * 统计所有为 1 的位的个数
     * @return
     */
    public int cardinality() {
        int count = 0;
        for (byte b : bytes) {
            if (b == 0) {
                continue;
            }
            if (b == -1) {
                count += 8;
                continue;
            }
            if ((b & 0x80) == 0x80) count++;
            if ((b & 0x40) == 0x40) count++;
            if ((b & 0x20) == 0x20) count++;
            if ((b & 0x10) == 0x10) count++;
            if ((b & 0x08) == 0x08) count++;
            if ((b & 0x04) == 0x04) count++;
            if ((b & 0x02) == 0x02) count++;
            if ((b & 0x01) == 0x01) count++;
        }
        return count;
    }

    /**
     * 两个 BitSet 进行按位与运算（此 BitSet 的多余的位会置 0）
     * @param bitSet
     */
    public void and(BitSet bitSet) {
        byte[] bs = bitSet.bytes;
        if (bs.length < bytes.length) {
            for (int i = 0; i < bs.length; i++) {
                bytes[ i ] &= bs[ i ];
            }
            for (int i = bs.length; i < bytes.length; i++) {
                bytes[ i ] = 0;
            }
            return;
        }
        for (int i = 0; i < bytes.length; i++) {
            bytes[ i ] &= bs[ i ];
        }
    }

    /**
     * 两个 BitSet 进行按位或运算（取长度较大的 BitSet 做为此 BitSet 再进行运算）
     * @param bitSet
     */
    public void or(BitSet bitSet) {
        byte[] bs = bitSet.bytes;
        if (bs.length > bytes.length) {
            bs = this.bytes;
            this.bytes = bitSet.bytes;
        }
        for (int i = 0; i < bs.length; i++) {
            bytes[ i ] |= bs[ i ];
        }
    }

    /**
     * 两个 BitSet 进行按位异或运算（取长度较大的 BitSet 做为此 BitSet 再进行运算）
     * @param bitSet
     */
    public void xor(BitSet bitSet) {
        byte[] bs = bitSet.bytes;
        if (bs.length > bytes.length) {
            bs = this.bytes;
            this.bytes = bitSet.bytes;
        }
        for (int i = 0; i < bs.length; i++) {
            bytes[ i ] ^= bs[ i ];
        }
    }

    /**
     * 返回此 BitSet 最小占用空间的位数
     * @return
     */
    public int size() {
        int len = bytes.length;
        if (len == 1) {
            return len << 3;
        }
        while (--len > 0 && bytes[ len ] == 0);
        return (len + 1) << 3;
    }

    /**
     * 返回此 BitSet 实际占用空间的位数
     * @return
     */
    public int length() {
        return bytes.length << 3;
    }

    /**
     * 缩小此 BitSet 实际占用空间到最小占用空间
     */
    public void reduceSize() {
        byte[] bs = new byte[ size() >>> 3 ];
        for (int j = 0; j < bs.length; j++) {
            bs[ j ] = bytes[ j ];
        }
        bytes = bs;
    }

    public byte[] toByteArray(){
        return bytes.clone();
    }

    public BitSet clone() {
        return new BitSet(bytes.clone());
    }

    @Override
    public String toString() {
        return new String(bytes);
    }

    public BitSet() {
        this.bytes = new byte[ 1 ];
    }

    public BitSet(int initLength) {
        this.bytes = new byte[ initLength >> 3 ];
    }

    public BitSet(byte[] bytes) {
        this.bytes = bytes;
    }

    public BitSet(String str) {
        this.bytes = str.getBytes();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        BitSet bitSet = (BitSet) o;
        return Arrays.equals(bytes, bitSet.bytes);
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(bytes);
    }

    /**
     * 扩大此 BitSet 空间
     * @param newLen
     */
    private void enlargeSize(int newLen) {
        byte[] bs = new byte[ newLen ];
        for (int j = 0; j < bytes.length; j++) {
            bs[ j ] = bytes[ j ];
        }
        bytes = bs;
    }

    /**
     * 判断 BitSet 是否为空
     * @param bitSet
     * @return
     */
    public static boolean isEmpty(BitSet bitSet){
        return bitSet != null && bitSet.size() == 8 && bitSet.bytes[ 0 ] == 0;
    }

    public static boolean get(byte[] bytes, int index){
        return (bytes[ index >>> 3 ] & SETGET[ index & 0x07 ]) != 0;
    }

    public static void set(byte[] bytes, int index, boolean value){
        int i = index >>> 3;
        if (value) {
            bytes[ i ] = (byte) (bytes[ i ] | SETGET[ index & 0x07 ]);
        } else {
            bytes[ i ] = (byte) (bytes[ i ] & CLEAR[ index & 0x07 ]);
        }
    }
}
